package com.hxzy.framework.web.security.filter;

import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.hxzy.common.constant.CacheConstants;
import com.hxzy.common.core.domain.model.LoginUser;
import com.hxzy.common.core.redis.RedisCache;
import com.hxzy.common.enums.AckCode;
import com.hxzy.common.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 验证jwt验证是否有效,与外卖JwtValidInterceptor几乎一样
 * (使用Filter来实现)
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    /**
     * 20分钟
     */
    private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;

    // 令牌有效期（默认60分钟）
    @Value("${token.expireTime}")
    private int expireTime;

    /**
     * 请求头参数
     */
    @Value(value = "${token.header}")
    private String  header;

    private final String bearer="Bearer";

    @Autowired
    private RedisCache redisCache;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        System.out.println("------------------jwt拦截器--------------------------");

        String jwtToken=request.getHeader(header);
        //1、有令牌,如果没有令牌，不要我们自己处理了，交给spring security后续拦截器去处理
        if(StrUtil.isNotBlank(jwtToken)){

            if(jwtToken.startsWith(bearer)){
                jwtToken=jwtToken.substring(bearer.length()+1).trim();
            }

            //jwt解密
            JWT jwt = JWTUtil.parseToken(jwtToken);
            //4、取值
            String uuid=jwt.getPayload("uuid").toString();
            //5、拼接rediskey
            String redisKey= CacheConstants.LOGIN_TOKEN_KEY+uuid;
            //6、查询redis的值
            LoginUser  loginUser=this.redisCache.getCacheObject(redisKey);
            //7、redis中当前用户如果不为null.但是springsecurity本地线程池中对象为空, httpsession无效的
            if(Objects.nonNull(loginUser) && Objects.isNull( SecurityUtils.getAuthentication())){
                //额外的功能，用户操作是否对令牌重新刷新值
                //verifyToken(loginUser);

                //8、要把这个对象 放到 spring security的本地线程池中 SecurityContextHolder
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
                authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }

        //交给其它拦截器来做
        filterChain.doFilter(request, response);
    }

    /**
     * 验证令牌有效期，相差不足20分钟，自动刷新缓存
     *
     * @param loginUser
     * @return 令牌
     */
    private void verifyToken(LoginUser loginUser)
    {
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
        {
            // 重新更新redis键把时间从现在开始，加上过期时间1小时
            refreshToken(loginUser);
        }
    }

    /**
     * 刷新令牌有效期
     *
     * @param loginUser 登录信息
     */
     private void refreshToken(LoginUser loginUser){
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * 60L*1000);
        // 根据uuid将loginUser缓存
        String userKey = CacheConstants.LOGIN_TOKEN_KEY+loginUser.getToken();
        //重新保存到redis中
        redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
    }

}
